import ActionMessage from "./ActionMessage"
import clientFunc from "./client.func"
import ResponseObserverAdaptor from "./ResponseObserverAdaptor"

class FunctionFacade {
  functions: FunctionRegistration
  constructor(t: FunctionRegistration) {
    this.functions = t
  }
  receiveResponse(t) {
    this.functions.receiveResponse(t)
  }
}
export default class FunctionRegistration {
  messenger: any
  responseResolvers: {}
  currentMsgId: number
  routines: {}
  responseObserver: any
  constructor(t) {
    this.messenger = t
    this.responseResolvers = {}
    this.currentMsgId = 0
    this.routines = {}
    const e = new FunctionFacade(this)
    this.responseObserver = new ResponseObserverAdaptor(e)
    this.messenger.addObserver(this.responseObserver)
  }
  initRoutines(t, ...e) {
    for (const s of e) {
      const e = new s(t)
      this.routines[e.key] = e
    }
  }
  dispose() {
    this.messenger.removeObserver(this.responseObserver)
    for (const t in this.responseResolvers) {
      const e = this.responseResolvers[t]
      e && e.reject(`SDK client was disconnected before ${e.funcKey} was fulfilled`)
    }
  }
  register(t, e) {
    return (...s) => this.dispatchAction(t, e, ...s)
  }
  async dispatchAction(t, e, ...s) {
    return new Promise((i, r) => {
      this.responseResolvers[this.currentMsgId] = { funcKey: t, funcDef: e, resolve: i, reject: r }
      const o = this.getSubRoutineFromDef(e),
        a = (function (t, e, s = !1) {
          if (s) {
            const s = clientFunc.n(t, e, t.length - 1),
              i = t.length - 1
            return (s[t[i]] = e.slice(i)), s
          }
          return clientFunc.n(t, e)
        })(o ? o.argsDef.argNames : e.args, s, o ? o.argsDef.varArg : e.varArg),
        h = o ? o.buildMessageData(this.currentMsgId, a) : a
      this.messenger.send(new ActionMessage(this.currentMsgId, t, h)), ++this.currentMsgId
    })
  }
  async receiveResponse(t) {
    const e = this.responseResolvers[t.uid]
    if (e)
      if (t.success) {
        const s = this.getSubRoutineFromDef(e.funcDef)
        s ? e.resolve(await s.onResponse(t.uid, t.message)) : e.resolve(t.message)
      } else e.reject(t.error)
    delete this.responseResolvers[t.uid]
  }
  getSubRoutineFromDef(t) {
    if (t.subRoutine) {
      const e = this.routines[t.subRoutine]
      if (!e) throw new Error(`${t.subRoutine} sub-routine was not provided for ${t}`)
      return e
    }
    return null
  }
}
